package com.peanutplan.blog.controller;


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.peanutplan.blog.constant.WebConst;
import com.peanutplan.blog.dao.CollectionVoMapper;
import com.peanutplan.blog.dao.CommentVoMapper;
import com.peanutplan.blog.dao.ContentVoMapper;
import com.peanutplan.blog.dao.MetaVoMapper;
import com.peanutplan.blog.dao.UserVoMapper;
import com.peanutplan.blog.dto.LogActions;
import com.peanutplan.blog.dto.MetaDto;
import com.peanutplan.blog.dto.Types;
import com.peanutplan.blog.exception.TipException;
import com.peanutplan.blog.model.AjaxResult;
import com.peanutplan.blog.model.bo.CommentBo;
import com.peanutplan.blog.model.bo.RestResponseBo;
import com.peanutplan.blog.model.vo.CollectionVo;
import com.peanutplan.blog.model.vo.CollectionVoExample;
import com.peanutplan.blog.model.vo.CommentVo;
import com.peanutplan.blog.model.vo.CommentVoExample;
import com.peanutplan.blog.model.vo.ContentEsVo;
import com.peanutplan.blog.model.vo.ContentVo;
import com.peanutplan.blog.model.vo.ContentVoExample;
import com.peanutplan.blog.model.vo.MetaVo;
import com.peanutplan.blog.model.vo.OptionVo;
import com.peanutplan.blog.model.vo.UserVo;
import com.peanutplan.blog.model.vo.UserVoExample;
import com.peanutplan.blog.service.ICommentService;
import com.peanutplan.blog.service.IContentService;
import com.peanutplan.blog.service.ILogService;
import com.peanutplan.blog.service.IMetaService;
import com.peanutplan.blog.service.IOptionService;
import com.peanutplan.blog.service.ISiteService;
import com.peanutplan.blog.service.IUserService;
import com.peanutplan.blog.utils.CheckUserAgent;
import com.peanutplan.blog.utils.CommonUtil;
import com.peanutplan.blog.utils.DateUtil;
import com.peanutplan.blog.utils.ElasticSearchUtils;
import com.peanutplan.blog.utils.IPKit;
import com.peanutplan.blog.utils.MatchTypes;
import com.peanutplan.blog.utils.SearchField;
import com.vdurmont.emoji.EmojiParser;

/**
 * 首页
 * Created by Administrator on 2017/3/8 008.
 */
@Controller
public class IndexController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class);

    @Resource
    private CollectionVoMapper collectionVoMapper;
    
    @Resource
    private IContentService contentService;

    @Resource
    private ICommentService commentService;

    @Resource
    private IMetaService metaService;

    @Resource
    private ISiteService siteService;
    
    @Resource
    private ContentVoMapper contentDao;

    @Resource
    private CommentVoMapper commentDao;
    
    @Resource
    private IOptionService optionService;
    
    @Resource
    private UserVoMapper userVoMapper;
    
    @Resource
    private IUserService usersService;
    
    @Resource
    private UserVoMapper userDao;

    @Resource
    private ILogService logService;
    
    @Resource
    private IMetaService metasService;
    
    @Resource
    private MetaVoMapper metaDao;
    
    @Value("${match_type:match_phrase}")
    private String matchType;
    
//    private Integer maxSearchSize;
    
    @GetMapping(value = {"login"})
    public String login(HttpServletRequest request, HttpServletResponse response) {
    	String referer = request.getHeader("referer");
    	if(StringUtils.isNotBlank(referer) && !referer.contains("logout")) {
    		request.setAttribute("from", referer);
    	}
    	return this.render("login");
    }
    @GetMapping(value = {"regist"})
    public String regist() {
    	return this.render("regist");
    }

    @PostMapping(value = {"registUser"})
    @ResponseBody
    public AjaxResult regist(String username, String pwd) {
        if(StringUtils.isBlank(username) || StringUtils.isBlank(pwd)) {
        	return AjaxResult.error("用户名或密码不能为空");
        }else {
        	UserVoExample userVoExample = new UserVoExample();
        	userVoExample.createCriteria().andUsernameEqualTo(username);
        	if(userDao.countByExample(userVoExample)>0) {
        		return AjaxResult.error("用户名重复！");
        	}
        	
        	pwd = CommonUtil.MD5encode(username+pwd);
        	UserVo userVo = new UserVo();
        	userVo.setPassword(pwd);
        	userVo.setUsername(username);
        	if(userVoMapper.insertSelective(userVo)>0) {
        		return AjaxResult.success("成功");
        	}
        }
    	return AjaxResult.error();
    }
    
    @PostMapping(value = "login")
    @ResponseBody
    public AjaxResult doLogin(@RequestParam String username,
                                  @RequestParam String password,
                                  @RequestParam(required = false) String remeber_me,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {

        Integer error_count = cache.get("login_error_count");
        try {
            UserVo user = usersService.login(username, password);
            request.getSession().setAttribute(WebConst.LOGIN_SESSION_KEY, user);
            if (StringUtils.isNotBlank(remeber_me)) {
                CommonUtil.setCookie(response, user.getUid());
            }
            logService.insertLog(LogActions.LOGIN.getAction(), null, request.getRemoteAddr(), user.getUid());
            
            user.setLogged(DateUtil.getCurrentUnixTime());
            userDao.updateByPrimaryKeySelective(user);
            
            String from = request.getParameter("from");
            return AjaxResult.success(from);
        } catch (Exception e) {
            error_count = null == error_count ? 1 : error_count + 1;
            if (error_count > 3) {
                return AjaxResult.error("您输入密码已经错误超过3次，请10分钟后尝试");
            }
            cache.set("login_error_count", error_count, 10 * 60);
            String msg = "登录失败";
            if (e instanceof TipException) {
                msg = e.getMessage();
            } else {
                LOGGER.error(msg, e);
            }
            return AjaxResult.error(msg);
        }
    }

    
    /**
     * 首页
     *
     * @return
     */
    @GetMapping(value = {"/", "index"})
    public String index(HttpServletRequest request) {
    	//简单统计
    	Integer articleSize = contentDao.countByExample(null);
    	CommentVoExample commentVoExample = new CommentVoExample();
    	commentVoExample.createCriteria().andTypeEqualTo("comment").andStatusEqualTo("approved").andTypeEqualTo(Types.ARTICLE.getType());;
    	Long commentSize = commentDao.countByExample(commentVoExample);
    	request.setAttribute("articleSize", articleSize.toString());
    	request.setAttribute("commentSize", commentSize.toString());
    	
        //个人名片
    	Map<String, String> options = new HashMap<>();
    	List<OptionVo> voList = optionService.getOptions();
        voList.forEach((option) -> {
            options.put(option.getName(), option.getValue());
        });
      //网站信息
        request.setAttribute("options", options);
      //点击排行
        List<ContentVo> topRead = this.topRead(7);
        if(topRead.size()>=1) {
        	request.setAttribute("topRead1", topRead.get(0));
        }
        request.setAttribute("topRead", topRead);
        
      //最新推荐
        request.setAttribute("nearTj", this.nearTj(4));
        
      //最新推荐
        request.setAttribute("topTj", this.nearTj(8));
        
      //最新置顶
        request.setAttribute("nearTop", this.nearTop(2));
        
      //最新置顶
        request.setAttribute("gonggao", this.nearPublish(5, "网站公告"));
        
      //猜你喜欢，随机取几条数据
        List<ContentVo> rand = contentDao.selectRand(10);
        request.setAttribute("rand", rand);
        
        //友链
        List<MetaVo> metas = metasService.getMetas(Types.LINK.getType());
        request.setAttribute("links", metas);
    	
        //分栏目推荐
        List<MetaDto> categories = metasService.getMetaList(Types.CATEGORY.getType(), null, 5);
        for(MetaDto c: categories) {
        	c.setArticles(this.nearPublish(5, c.getName()));
        }
        request.setAttribute("categoriesTop", categories);
        
        request.setAttribute("nearNormal", nearNormal(10, null));
        
    	return this.render("index");
    }

    @RequestMapping(value = {"navigation", "navigation.html"})
    public String navigation(HttpServletRequest request) {
    	//点击排行
        List<ContentVo> topRead = this.topRead(7);
        if(topRead.size()>=1) {
        	request.setAttribute("topRead1", topRead.get(0));
        }
        request.setAttribute("topRead", topRead);
        
        List<ContentVo> rand = contentDao.selectRand(10);
        request.setAttribute("rand", rand);
        
        //网站信息
        request.setAttribute("webInfo", this.webInfo());
        
        return this.render("navigation");
    }
    
    /**
     * 首页分页
     *
     * @param request request
     * @param p       第几页
     * @param limit   每页大小
     * @return 主页
     */
    @GetMapping(value = {"list"})
    public String index(HttpServletRequest request, Integer page, @RequestParam(value = "limit", defaultValue = "12") int limit, Integer categoryId, Integer tagId, String category, String tag) {
    	page = page==null?1:page;
    	page = page < 0 || page > WebConst.MAX_PAGE ? 1 : page;
    	
    	if(categoryId!=null) {
    		MetaVo categoryMeta = metaDao.selectByPrimaryKey(categoryId);
    		category = categoryMeta==null?"":categoryMeta.getName();
    		request.setAttribute("categoryId", categoryId);
    	}
    	if(tagId!=null) {
    		MetaVo tagMeta = metaDao.selectByPrimaryKey(tagId);
        	tag = tagMeta==null?"":tagMeta.getName();
    	}
    	
        PageInfo<ContentVo> articles = contentService.getContents(page, limit, category, tag);
        request.setAttribute("articles", articles);
        
        if(StringUtils.isNotBlank(category)) {
        	request.setAttribute("category", category);
        }
        if(StringUtils.isNotBlank(tag)) {
        	request.setAttribute("tag", tag);
        }
        
      //点击排行
        List<ContentVo> topRead = this.topRead(7);
        if(topRead.size()>=1) {
        	request.setAttribute("topRead1", topRead.get(0));
        }
        request.setAttribute("topRead", topRead);
        
      //猜你喜欢，随机取几条数据
        List<ContentVo> rand = contentDao.selectRand(10);
        request.setAttribute("rand", rand);
        
      //标签
        List<MetaDto> allTags = this.allTags(20);
        request.setAttribute("allTags", allTags);
        
      //简单统计
    	Integer articleSize = contentDao.countByExample(null);
    	CommentVoExample commentVoExample = new CommentVoExample();
    	commentVoExample.createCriteria().andTypeEqualTo("comment").andStatusEqualTo("approved");
    	Long commentSize = commentDao.countByExample(commentVoExample);
    	request.setAttribute("articleSize", articleSize.toString());
    	request.setAttribute("commentSize", commentSize.toString());
    	
      //个人名片
    	List<OptionVo> voList = optionService.getOptions();
        Map<String, String> options = new HashMap<>();
        voList.forEach((option) -> {
            options.put(option.getName(), option.getValue());
        });
        request.setAttribute("options", options);
        
        return this.render("list");
    }

    /**
     * 文章页
     *
     * @param request 请求
     * @param cid     文章主键
     * @return
     */
    @RequestMapping(value = {"article/{cid}", "article/{cid}.html"})
    public String getArticle(HttpServletRequest request, @PathVariable String cid) {
    	ContentVo article = contentService.getContents(cid);
    	if (null == article || "draft".equals(article.getStatus())) {
            return this.render_404();
        }
    	request.setAttribute("original_title", article.getTitle());
    	String title = article.getTitle();
    	String content = article.getContent();
    	
    	Map<String, String> requestParams = getParameterMap(request);
    	if(requestParams.get("from")!=null) {
    		String keyword = requestParams.get("keyword");
    		String matchType = requestParams.get("matchType");
    		
    		//match拿全文
			if("match".equals(matchType)) {
				
	    		Map<String,Object> shouldMap = new HashMap<>(2,1.0F);
	            shouldMap.put("content", keyword);
	            shouldMap.put("title", keyword);
	            
	    		Map<String, Object> mustMap = new HashMap<String, Object>(1,1.0F);
	    		mustMap.put("cid", cid);
	    		Map<String, HighlightField> r = ElasticSearchUtils.searchSingleDocsHighlight("article", mustMap, shouldMap, matchType);
	    		Set<Entry<String, HighlightField>> entrySet= r.entrySet();
	    		for(Entry<String, HighlightField> entry:entrySet) {
	    			//字段名称
	    			String field = entry.getKey();
	    			//高亮片段
	    			Text[] fragments = entry.getValue().getFragments();
	    			
					String highlight = fragments[0].toString();
					if("content".equals(field)) {
						content = highlight;
					}
					if("title".equals(field)) {
						title = highlight;
					}
	    		}
			}else {
				//短语匹配替换就行了
				String[] ss = keyword.toString().split("\\s+");
				if(ss.length>1) {
					for(String s:ss) {
						content = content.replaceAll(s, "<em>"+s+"</em>");
					}
				}else {
					content = content.replaceAll(keyword, "<em>"+keyword+"</em>");
				}
			}
    		
    	}
    	article.setContent(content);
    	article.setTitle(title);
    	
        
        //文章基本属性
        if(StringUtils.isNotBlank(article.getTags())) {
        	request.setAttribute("articleTags", article.getTags().split(","));
        }else {
        	request.setAttribute("articleTags", Collections.EMPTY_LIST);
        }
        request.setAttribute("article", article);
        request.setAttribute("is_post", true);
        completeArticle(request, article);
//        updateArticleHit(contents);
        
        //上一篇下一篇
        ContentVo pre = contentService.getPreContents(article);
        ContentVo next = contentService.getNextContents(article);
        request.setAttribute("pre", pre);
        request.setAttribute("next", next);
        
        //点击排行
        List<ContentVo> topRead = this.topRead(7);
        if(topRead.size()>=1) {
        	request.setAttribute("topRead1", topRead.get(0));
        }
        request.setAttribute("topRead", topRead);
        
        //本栏推荐
        List<ContentVo> categoryPutTop = this.categoryPutTop(article.getCategories(), 7);
        if(categoryPutTop.size()>=1) {
        	request.setAttribute("categoryPutTop1", categoryPutTop.get(0));
        }
        request.setAttribute("categoryPutTop", categoryPutTop);
        
        //标签
        List<MetaDto> allTags = this.allTags(20);
        request.setAttribute("allTags", allTags);
        
        //猜你喜欢，随机取几条数据
        List<ContentVo> rand = contentDao.selectRand(10);
        request.setAttribute("rand", rand);
        
        //网站信息
        request.setAttribute("webInfo", this.webInfo());
        
        //最近100条评论
        CommentVoExample commentVoExample = new CommentVoExample();
        commentVoExample.createCriteria().andCidEqualTo(article.getCid());
        commentVoExample.setOrderByClause("created desc");
        commentVoExample.setLimit(50);
        request.setAttribute("comments", commentDao.selectByExampleWithBLOBs(commentVoExample));
        
        return this.render("post");
    }

    /**
     * 点击排行
     * <p>Title: topRead</p>  
     * <p>Description: </p>
     * @author 陈阳  
     * @date 2019年4月24日
     * @param limit
     * @return
     */
    @GetMapping(value = "topRead")
    @ResponseBody
    public List<ContentVo> topRead(@RequestParam(value = "limit", defaultValue = "8") int limit) {
    	PageHelper.startPage(1, limit);
    	
    	ContentVoExample contentVoExample = new ContentVoExample();
    	contentVoExample.createCriteria().andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());
        contentVoExample.setOrderByClause("hits desc, created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
    	
    	return contentVos;
    }
    
    /**
     * 本栏推荐
     * <p>Title: topRead</p>  
     * <p>Description: </p>
     * @author 陈阳  
     * @date 2019年4月24日
     * @param limit
     * @return
     */
    @GetMapping(value = "categoryPutTop")
    @ResponseBody
    public List<ContentVo> categoryPutTop(String category, @RequestParam(value = "limit", defaultValue = "8") int limit) {
    	PageHelper.startPage(1, limit);
    	
    	ContentVoExample contentVoExample = new ContentVoExample();
    	ContentVoExample.Criteria c = contentVoExample.createCriteria();
    	c.andAllowPingEqualTo(Boolean.TRUE);
    	c.andTypeEqualTo(Types.ARTICLE.getType());
    	c.andStatusEqualTo(Types.PUBLISH.getType());
    	if(StringUtils.isNoneBlank(category)) {
    		c.andCategoriesLike("%"+category+"%");
    	}
        contentVoExample.setOrderByClause("hits desc, created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
    	
    	return contentVos;
    }

    /**
     * 标签云
     * <p>Title: allTags</p>  
     * <p>Description: </p>
     * @author 陈阳  
     * @date 2019年4月24日
     * @param category
     * @param limit
     * @return
     */
    @GetMapping(value = "allTags")
    @ResponseBody
    public List<MetaDto> allTags(@RequestParam(value = "limit", defaultValue = "8") int limit) {
    	List<MetaDto> tags = metaService.getMetaList(Types.TAG.getType(),  null, limit);
    	return tags;
    }
    
    /**
     * 网站信息
     * <p>Title: allTags</p>  
     * <p>Description: </p>
     * @author 陈阳  
     * @date 2019年4月24日
     * @param category
     * @param limit
     * @return
     */
    @GetMapping(value = "webInfo")
    @ResponseBody
    public Map<String, Object> webInfo() {
    	Map<String, Object> map = new HashMap<>();
    	
    	int articleSize = contentDao.countByExample(null);
    	
    	CommentVoExample commentVoExample = new CommentVoExample();
    	commentVoExample.createCriteria().andTypeEqualTo("comment").andStatusEqualTo("approved");
    	long commentSize = commentDao.countByExample(commentVoExample);
    	
    	CommentVoExample commentVoExample2 = new CommentVoExample();
    	commentVoExample2.createCriteria().andTypeEqualTo("message").andStatusEqualTo("approved");
    	long messageSize = commentDao.countByExample(commentVoExample2);
    	
    	List<OptionVo> voList = optionService.getOptions();
        Map<String, String> options = new HashMap<>();
        voList.forEach((option) -> {
            options.put(option.getName(), option.getValue());
        });
        
        map.put("options", options);
        map.put("articleSize", articleSize);
        map.put("commentSize", commentSize);
        map.put("messageSize", messageSize);
    	
    	return map;
    }
    
    /**
     * 抽取公共方法
     *
     * @param request
     * @param contents
     */
    private void completeArticle(HttpServletRequest request, ContentVo contents) {
        if (contents.getAllowComment()!=null && contents.getAllowComment()) {
            String cp = request.getParameter("cp");
            if (StringUtils.isBlank(cp)) {
                cp = "1";
            }
            request.setAttribute("cp", cp);
            PageInfo<CommentBo> commentsPaginator = commentService.getComments(contents.getCid(), Integer.parseInt(cp), 6);
            request.setAttribute("comments", commentsPaginator);
        }
    }

    /**
     * 评论操作
     */
    @PostMapping(value = "comment")
    @ResponseBody
    @Transactional(rollbackFor = TipException.class)
    public RestResponseBo<?> comment(HttpServletRequest request, HttpServletResponse response,
                                  @RequestParam Integer cid, @RequestParam String text) {

        if (null == cid || StringUtils.isBlank(text)) {
            return RestResponseBo.fail("请输入完整后评论");
        }
        if (text.length() > 200) {
            return RestResponseBo.fail("请输入200个字符以内的评论");
        }

        String val = IPKit.getIpAddrByRequest(request) + ":" + cid;
        Integer count = cache.hget(Types.COMMENTS_FREQUENCY.getType(), val);
        if (null != count && count > 0) {
            return RestResponseBo.fail("您发表评论太快了，请过会再试");
        }

        text = CommonUtil.cleanXSS(text);

        text = EmojiParser.parseToAliases(text);
        UserVo u = (UserVo) request.getSession().getAttribute(WebConst.LOGIN_SESSION_KEY);;
        CommentVo comments = new CommentVo();
        comments.setAuthor(u.getUsername());
        comments.setCid(cid);
        comments.setIp(request.getRemoteAddr());
        comments.setContent(text);
        try {
            commentService.insertComment(comments);
            // 设置对每个文章1分钟可以评论一次
            cache.hset(Types.COMMENTS_FREQUENCY.getType(), val, 1, 60);
            return RestResponseBo.ok();
        } catch (Exception e) {
        	e.printStackTrace();
            String msg = "评论发布失败";
            if (e instanceof TipException) {
                msg = e.getMessage();
            } else {
                LOGGER.error(msg, e);
            }
            return RestResponseBo.fail(msg);
        }
    }
    
    /**
     * 收藏
     * <p>Title: follow</p>  
     * <p>Description: </p>
     * @author 陈阳  
     * @date 2019年4月26日
     * @param request
     * @param response
     * @param cid
     * @return
     */
    @PostMapping(value = "follow")
    @ResponseBody
    @Transactional(rollbackFor = TipException.class)
    public RestResponseBo<?> follow(HttpServletRequest request, HttpServletResponse response,
                                  @RequestParam Integer cid) {

    	UserVo user = this.user(request);
    	if(user==null) {
    		return RestResponseBo.fail("请先登录");
    	}
        try {
        	CollectionVoExample collectionVoExample = new CollectionVoExample();
        	collectionVoExample.createCriteria().andCidEqualTo(cid).andUidEqualTo(user.getUid());
        	int count = collectionVoMapper.countByExample(collectionVoExample);
        	if(count>0) {
        		return RestResponseBo.ok("你已经收藏过了");
        	}
        	
        	CollectionVo collectionVo = new CollectionVo();
        	collectionVo.setCid(cid.intValue());
        	collectionVo.setUid(user.getUid());
        	collectionVo.setCreated((int) DateUtil.getUnixTimeLong());
//        	collectionVo.setTitle(contentDao.selectByPrimaryKey(cid).getTitle());
        	collectionVoMapper.insert(collectionVo);
            return RestResponseBo.ok();
        } catch (Exception e) {
            String msg = "收藏失败";
            if (e instanceof TipException) {
                msg = e.getMessage();
            } else {
                LOGGER.error(msg, e);
            }
            return RestResponseBo.fail(msg);
        }
    }

    /**
     * 最新推荐
     * @param limit
     * @return
     */
    @GetMapping(value = "nearTj")
    @ResponseBody
    public List<ContentVo> nearTj(@RequestParam(value = "limit", defaultValue = "4") int limit) {
    	PageHelper.startPage(1, limit);
    	ContentVoExample contentVoExample = new ContentVoExample();
    	contentVoExample.createCriteria().andCategoriesEqualTo("特别推荐").andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());
        contentVoExample.setOrderByClause("created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
        PageHelper.clearPage();
        if(contentVos.size()<limit) {
        	PageHelper.startPage(1, limit-contentVos.size());
        	ContentVoExample contentVoExample2 = new ContentVoExample();
        	contentVoExample2.createCriteria().andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());
            contentVoExample2.setOrderByClause("created desc");
            List<ContentVo> contentVos2 = contentDao.selectByExample(contentVoExample2);
            contentVos.addAll(contentVos2);
            PageHelper.clearPage();
        }
    	
    	return contentVos;
    }
    
    /**
     * 最新推荐
     * @param limit
     * @return
     */
    @GetMapping(value = "nearTop")
    @ResponseBody
    public List<ContentVo> nearTop(@RequestParam(value = "limit", defaultValue = "4") int limit) {
    	PageHelper.startPage(1, limit);
    	ContentVoExample contentVoExample = new ContentVoExample();
    	contentVoExample.createCriteria().andAllowPingEqualTo(Boolean.TRUE).andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());
        contentVoExample.setOrderByClause("created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
        PageHelper.clearPage();
        if(contentVos.size()<limit) {
        	PageHelper.startPage(1, limit-contentVos.size());
        	ContentVoExample contentVoExample2 = new ContentVoExample();
            contentVoExample2.setOrderByClause("created desc");
            List<ContentVo> contentVos2 = contentDao.selectByExample(contentVoExample2);
            contentVos.addAll(contentVos2);
            PageHelper.clearPage();
        }
    	
    	return contentVos;
    }
    
    /**
     * 最新发布
     * @param limit
     * @return
     */
    @GetMapping(value = "nearPublish")
    @ResponseBody
    public List<ContentVo> nearPublish(@RequestParam(value = "limit", defaultValue = "2") int limit, String categories) {
    	PageHelper.startPage(1, limit);
    	
    	ContentVoExample contentVoExample = new ContentVoExample();
    	ContentVoExample.Criteria c = contentVoExample.createCriteria();
    	if(StringUtils.isNotBlank(categories)) {
    		c.andCategoriesEqualTo(categories);
    	}
    	c.andTypeEqualTo(Types.ARTICLE.getType());
    	c.andStatusEqualTo(Types.PUBLISH.getType());
    	
        contentVoExample.setOrderByClause("created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
    	
    	return contentVos;
    }
    
    /**
     * 先置顶再时间排序
     * @param limit
     * @param categories
     * @return
     */
    @GetMapping(value = "nearNormal")
    @ResponseBody
    public List<ContentVo> nearNormal(@RequestParam(value = "limit", defaultValue = "2") int limit, String categories) {
    	PageHelper.startPage(1, limit);
    	
    	ContentVoExample contentVoExample = new ContentVoExample();
    	ContentVoExample.Criteria c = contentVoExample.createCriteria();
    	if(StringUtils.isNotBlank(categories)) {
    		c.andCategoriesEqualTo(categories);
    	}
    	c.andTypeEqualTo(Types.ARTICLE.getType());
    	c.andStatusEqualTo(Types.PUBLISH.getType());
        contentVoExample.setOrderByClause("allow_ping desc, created desc");
        List<ContentVo> contentVos = contentDao.selectByExample(contentVoExample);
    	
    	return contentVos;
    }

    /**
     * 自定义页面,如关于的页面
     */
    @GetMapping(value = "/page/{pagename}")
    public String page(@PathVariable String pagename, HttpServletRequest request) {
    	ContentVo contents = contentService.getContents(pagename);
        if (null == contents || "draft".equals(contents.getStatus())) {
            return this.render_404();
        }
        
        //文章基本属性
        request.setAttribute("article", contents);
        request.setAttribute("is_post", true);
        completeArticle(request, contents);
        
        return this.render("page");
    }
    
    /**
     * 搜索页
     *
     * @param keyword
     * @return
     */
    @RequestMapping(value = "search")
    public String search(HttpServletRequest request, String keyword, Integer limit, @RequestParam(value = "type", required = false) String type, String categories, String tags) {
    	String userAgent = request.getHeader("user-agent");
        request.setAttribute("isMobile", CheckUserAgent.checkAgentIsMobile(userAgent));
    	
    	Map<String, String> options = new HashMap<>();
    	List<OptionVo> voList = optionService.getOptions();
        voList.forEach((option) -> {
            options.put(option.getName(), option.getValue());
        });
        //网站信息
        request.setAttribute("options", options);
    	Integer maxSearxhSize = 20;
    	if(options.get("site_max_search_size")!=null && StringUtils.isNotBlank(options.get("site_max_search_size").toString())) {
    		maxSearxhSize = Integer.parseInt(options.get("site_max_search_size").toString());
    	}
    	limit = (limit==null||limit<0||limit>1000)?maxSearxhSize:limit;
    	return this.search(request, keyword, 1, limit, type, categories, tags);
    }

    public String search(HttpServletRequest request, String keyword, int page, Integer limit, String type, String categories, String tags) {
        page = page < 0 || page > WebConst.MAX_PAGE ? 1 : page;
        
        List<SearchField> should = new ArrayList<>(2);
        SearchField titleSearch = new SearchField("title", keyword, MatchTypes.MATCH_PHRASE);
        SearchField contentTextSearch = new SearchField("contentText", keyword);
        should.add(titleSearch);
        
        String searchType = matchType;
        if(StringUtils.isNotBlank(type) && type.equals("精确")) {
        	searchType = MatchTypes.MATCH_PHRASE;
        	request.setAttribute("type", "精确");
        }else {
        	searchType = MatchTypes.MATCH;
        	request.setAttribute("type", "模糊");
        }
        contentTextSearch.setSearchType(searchType);
        should.add(contentTextSearch);
        
        List<SearchField> must = new ArrayList<>(2);
        if(StringUtils.isNotBlank(categories)) {
        	SearchField categoriesSearch = new SearchField("categories", categories.split(","), MatchTypes.MATCH_PHRASE);
        	must.add(categoriesSearch);
        }
        if(StringUtils.isNotBlank(tags)) {
        	SearchField tagsSearch = new SearchField("tags", tags.split(","), MatchTypes.MATCH_PHRASE);
        	must.add(tagsSearch);
        }
        
        //分词查询
        List<Map<String, Object>> result = ElasticSearchUtils.multiOrSearchDocWithHighlight("article", must, should, null, ContentEsVo.class, null, 0, limit);
        
        //不分词查询 
//        List<Map<String, Object>> keywordResult = ElasticSearchUtils.multiOrSearchDocWithHighlight("article", mustMap, shouldMap, null, ContentEsVo.class, null, 0, 5, MatchTypes.MATCH_PHRASE);
        
        
        List<ContentEsVo> articles = new ArrayList<ContentEsVo>(result.size());
        buildResult(articles, result);
        
//        List<ContentEsVo> keywordArticles = new ArrayList<ContentEsVo>(keywordResult.size()+result.size());
//        buildResult(keywordArticles, keywordResult);
        //从分词查询中把不分词查询结果去掉
//        articles = listrem(articles, keywordArticles);
//        keywordArticles.addAll(articles);
        
        Integer allHits = CollectionUtils.isEmpty(result)?0:Integer.parseInt(result.get(0).get("allHits").toString());
		//没有结果时候给出分词查询建议
		if(allHits==0 && keyword.length()>2) {
			//分词结果
			List<String> keywordAnalyze = ElasticSearchUtils.getAnalyze(keyword);
			request.setAttribute("keywordAnalyze", keywordAnalyze);
		}
		request.setAttribute("searchCategories", categories);
    	request.setAttribute("searchTags", tags==null?"":tags);
		request.setAttribute("articles", articles);
		request.setAttribute("allHits", allHits);
        request.setAttribute("keyword", keyword);
        request.setAttribute("matchType", searchType);
        request.setAttribute("limit", limit);
        return this.render("search");
    }
    
    private void buildResult(List<ContentEsVo> articles, List<Map<String, Object>> result) {
    	for(Map<String, Object> r:result) {
			ContentEsVo vo = (ContentEsVo) r.get("bean");
			vo.setContentText(vo.getRemark());
			@SuppressWarnings("unchecked")
			Map<String, String> highlight = (Map<String, String>) r.get("highlight");
			if(highlight.get("title")!=null) {
				vo.setTitle(highlight.get("title"));
			}
			if(highlight.get("contentText")!=null) {
				vo.setContentText(highlight.get("contentText"));
			}
			articles.add(vo);
		}
    }
    
//    private static List<ContentEsVo> listrem(List<ContentEsVo> listA,List<ContentEsVo> listB){
//        LinkedHashSet<ContentEsVo> hs1 = new LinkedHashSet<ContentEsVo>(listA);
//        LinkedHashSet<ContentEsVo> hs2 = new LinkedHashSet<ContentEsVo>(listB);
//        hs1.removeAll(hs2);
//        List<ContentEsVo> listC = new ArrayList<ContentEsVo>();
//        listC.addAll(hs1);
//        return listC;
//    }
    
    /** 
     * 从request中获得参数Map，并返回可读的Map 
     *  
     * @param request 
     * @return 
     */  
    public static Map<String, String> getParameterMap(HttpServletRequest request) {  
        // 参数Map  
        Map<String, String[]> properties = request.getParameterMap();  
        // 返回值Map  
        Map<String, String> returnMap = new HashMap<String, String>();  
        Iterator<Entry<String, String[]>> entries = properties.entrySet().iterator();  
        Map.Entry<String, String[]> entry;  
        String name = "";  
        String value = "";  
        while (entries.hasNext()) {  
            entry = (Entry<String, String[]>) entries.next();  
            name = (String) entry.getKey();  
            Object valueObj = entry.getValue();  
            if(null == valueObj){  
                value = "";  
            }else if(valueObj instanceof String[]){  
                String[] values = (String[])valueObj;  
                for(int i=0;i<values.length;i++){  
                    value = values[i] + ",";  
                }  
                value = value.substring(0, value.length()-1);  
            }else{  
                value = valueObj.toString();  
            }  
            returnMap.put(name, value);  
        }  
        return returnMap;  
    } 
}
